﻿using System;
using System.Windows;
using nRoute.Components;
using nRoute.Utilities;
using nRoute.Navigation;
using nRoute.Components.Routing;
using nRoute.Navigation.Containers;
using System.Collections.Generic;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
namespace nRoute.Controls
{
    public partial class ApplicationFrameContainer
         : INavigationHandler, ISupportRefreshNavigation, IBrowsingContainer, INavigationContainer, ISupportNavigationViewState
    {
        private const string NAVIGATIONURL_NOTNULLOREMPTY = "Request's navigation url cannot be null or empty.";
        private const string ABOUTFILE_FORMAT = "about:file/{0}";

        private EventHandler<Navigation.Containers.NavigatingCancelEventArgs> _navigatingHandler;
        private EventHandler<NavigatedEventArgs> _navigatedHandler;
        private EventHandler<Navigation.Containers.NavigationFailedEventArgs> _navigationFailedHandler;

#region INavigationContainer Members

        event EventHandler<Navigation.Containers.NavigatingCancelEventArgs> INavigationContainer.Navigating
        {
            add { _navigatingHandler += value; }
            remove { _navigatingHandler -= value; }
        }

        event EventHandler<NavigatedEventArgs> INavigationContainer.Navigated
        {
            add { _navigatedHandler += value; }
            remove { _navigatedHandler -= value; }
        }

        event EventHandler<Navigation.Containers.NavigationFailedEventArgs> INavigationContainer.NavigationFailed
        {
            add { _navigationFailedHandler += value; }
            remove { _navigationFailedHandler -= value; }
        }

        // the rest are inherited from the base class

#if (WP7)

        object INavigationContainer.Content
        {
            get { return base.Content; }
        }

#endif

#endregion
        
#region INavigationHandler Members

        void INavigationHandler.ProcessRequest(NavigationRequest request, Action<bool> requestCallback)
        {
            OnProcessRequest(request, requestCallback);
        }

        void INavigationHandler.ProcessResponse(NavigationResponse response)
        {
            OnProcessResponse(response);
        }

#endregion

#region IBrowsingContainer Members

        public event EventHandler<NavigationContainerEventArgs> JournalPurged;

        public void NavigateBack()
        {
            OnNavigateBack();
        }

        public void NavigateForward()
        {
            OnNavigateForward();
        }

        public bool CanNavigateBack
        {
            get { return OnCanNavigateBack(); }
        }

        public bool CanNavigateForward
        {
            get { return OnCanNavigateForward(); }
        }

        public bool CanPurgeJournal
        {
            get { return (OnCanNavigateBack() || OnCanNavigateForward()); }
        }

        public void PurgeJournal()
        {
            OnPurgeJournals();
        }

        public IEnumerable<PageContentState> BackPagesJournal 
        {
            get
            {
                return _backStack;
            }
        }

        public IEnumerable<PageContentState> ForwardPagesJournal 
        {
            get
            {
                return _forwardStack;
            }
        } 

#endregion

#region Overridable Methods

        protected virtual void OnUrlChanged(string oldUrl, string newUrl)
        {
            if (string.IsNullOrEmpty(newUrl)) return;
            var _currentUrl = CurrentNavigationRequest != null ? CurrentNavigationRequest.RequestUrl : null;

            // if our requested url is not equal to our current url then navigate
            if (!string.Equals(_currentUrl, newUrl, StringComparison.OrdinalIgnoreCase))
            {
                Navigate(newUrl);
            }
        }

        protected virtual void OnNavigate(NavigationRequest request)
        {
            Guard.ArgumentNotNull(request, "request");
            Guard.ArgumentValue(string.IsNullOrEmpty(request.RequestUrl), "request", NAVIGATIONURL_NOTNULLOREMPTY);

            Navigation.NavigationService.Navigate(request, this);
        }

        protected virtual void OnProcessRequest(NavigationRequest request, Action<bool> requestCallback)
        {
            Guard.ArgumentNotNull(request, "request");
            Guard.ArgumentNotNull(requestCallback, "requestCallback");

            // if there is a pending request
            if (ActiveNavigationRequest != null)
            {
                Navigation.NavigationService.CancelNavigation(ActiveNavigationRequest);
                ActiveNavigationRequest = null;
                ActiveNavigationResponse = null;
            }
            
            // we check if our current page supports ISupportNavigationLifecycle, if so we defer to it
            Navigation.NavigationService.ProcessNavigationLifecycle(request, CurrentNavigationLifecycleSupporter, CurrentNavigationViewLifecycleSupporter,
                (b) =>
                {
                    if (b)
                    {
                        ActiveNavigationRequest = request;
                    }
                    else
                    {
                        requestCallback(b);
                    }
                },
                () =>
                {
                    TransitionToNavigationState(Controls.NavigationState.Navigating);
                    requestCallback(true);
                });
        }

        protected virtual void OnProcessResponse(NavigationResponse response)
        {
            Guard.ArgumentNotNull(response, "response");

            // we won't handle it the response, if it is not the active request!
            if (!response.Request.Equals(ActiveNavigationRequest)) return;
            
            // we check if the navigation was successful or not
            if (response.Status != ResponseStatus.Success)
            {
                // if cancelled then return to normal state, else we inform the error 
                if (response.Status == ResponseStatus.Cancelled)
                {
                    TransitionToNavigationState(Controls.NavigationState.Navigated);
                    return;
                }
                ShowFailedNavigationState(response.Request, response.Status, response.Error);
                return;
            }

            // we save the response and reset the request
            ActiveNavigationResponse = response;

            // note the assumption here is that the response will give us an Uri - per the MapNavigationFileAttribute
            var _source = (Uri)response.Content;
            base.Navigate(_source);
        }

#endregion

#region ISupportRefreshNavigation Members

        public bool CanRefresh
        {
            get { return (CurrentNavigationRequest != null); }
        }

        public void Refresh()
        {
            if (CurrentNavigationRequest != null)
            {
                //var _refreshRequest = new NavigationRequest(CurrentNavigationRequest.RequestUrl, 
                //    CurrentNavigationRequest.RequestParameters, CurrentNavigationRequest.SiteArea, NavigateMode.Refresh);
                //Navigation.NavigationService.Navigate(_refreshRequest, this);
                base.Navigate(base.CurrentSource);
            }
        }

#endregion

#region Browsing Related Methods

        protected virtual void OnNavigateBack()
        {
            if (base.CanGoBack) base.GoBack();
        }

        protected virtual void OnNavigateForward()
        {
            if (base.CanGoForward) base.GoForward();
        }

        protected virtual bool OnCanNavigateBack()
        {
            return base.CanGoBack;
        }

        protected virtual bool OnCanNavigateForward()
        {
            return base.CanGoForward;
        }

        protected virtual void OnPurgeJournals()
        {
            // we don't support this because using this can easily make us go in a logically inconsistent state
            throw new NotSupportedException();            
        }

#endregion

#region ISupportNavigationViewState

        void ISupportNavigationViewState.RestoreState(ParametersCollection state)
        {
            OnRestoreState(state);
        }

        ParametersCollection ISupportNavigationViewState.SaveState()
        {
            return OnSaveState();
        }
        
#endregion

#region Navigating Events Related

        protected virtual void OnNavigating(Navigation.Containers.NavigatingCancelEventArgs e)
        {
            // we raise the event
            if (_navigatingHandler != null) _navigatingHandler(this, e);

            // - WE DON't Transition here as the thing might have been cancelled
            //TransitionToNavigationState(NavigationState.Navigating, true);
        }

        protected virtual void OnNavigationCompleted(Navigation.Containers.NavigatedEventArgs e)
        {
            // we raise the event
            if (_navigatedHandler != null) _navigatedHandler(this, e);

            // and indicate the visual state
            TransitionToNavigationState(Controls.NavigationState.Navigated, true);
        }

        protected virtual void OnNavigatingFailed(Navigation.Containers.NavigationFailedEventArgs e)
        {
            // we raise the event
            if (_navigationFailedHandler != null) _navigationFailedHandler(this, e);

            // and indicate the visual state
            if (e.Status != ResponseStatus.Cancelled)
            {
                ShowFailedNavigationState(e.Request, e.Status, e.Error);
                TransitionToNavigationState(Controls.NavigationState.NavigationFailed, true);
            }
            else
            {
                TransitionToNavigationState(Controls.NavigationState.Navigated);
            }
        }

 #endregion

#region Helpers

        private PageContentState GetCurrentPageContentState()
        {
            // we check if an intermediate state is available
            if (_intermediateState != null)
            {
                var _state = _intermediateState;
                _intermediateState = null;           // reset it
                return _state;
            }

            if (string.IsNullOrEmpty(this.Url)) return null;

            // we also add
            return new PageContentState(this.Url, CurrentNavigationRequest != null ? CurrentNavigationRequest.SiteArea : null, 
                (PageInfo != null) ? PageInfo.Title : null,
                (CurrentNavigationStateSupporter == null ? null : CurrentNavigationStateSupporter.SaveState()),
                (CurrentNavigationViewStateSupporter == null) ? null : CurrentNavigationViewStateSupporter.SaveState());
        }

        private void RaiseHistoryChanged()
        {
            this.NotifyPropertyChanged(() => CanNavigateBack);
            this.NotifyPropertyChanged(() => CanNavigateForward);
            this.NotifyPropertyChanged(() => CanPurgeJournal);
        }

#endregion

    }
}